package com.boot.security.service.database.impl;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.boot.cache.redis.serializer.SimpleRedisKeySerializer;
import com.boot.database.service.CurrentUserHandler;
import com.boot.persistence.RedisCacheServiceImpl;
import com.boot.security.mapper.UserMapper;
import com.boot.security.model.po.User;
import com.boot.security.service.database.UserService;
import com.boot.web.exception.GlobalException;
import com.boot.web.http.Status;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.http.HttpStatus;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 霜寒 <1621856595@qq.com>
 * @date 2020/2/14 21:50
 **/
@Service
public class UserServiceImpl extends RedisCacheServiceImpl<Long, User, UserMapper> implements UserService {
    private final PasswordEncoder encoder;
    private final CurrentUserHandler currentUserHandler;

    public UserServiceImpl(UserMapper mapper, RedisConnectionFactory redisConnectionFactory, ObjectMapper objectMapper, PasswordEncoder encoder, CurrentUserHandler currentUserHandler) {
        super(mapper, redisConnectionFactory, objectMapper, "Unused");
        this.encoder = encoder;
        this.currentUserHandler = currentUserHandler;
    }

    @Override
    public RedisSerializer<Long> getModelKeySerializer() {
        return new SimpleRedisKeySerializer<Long>("User") {

            @Override
            public String[] toArray(Long aLong) {
                return new String[]{String.valueOf(aLong)};
            }

            @Override
            public Long fromArray(String[] arr) {
                return Long.parseLong(arr[0]);
            }
        };
    }

    @Override
    public boolean save(User entity) {
        if (entity.getPassword() == null) {
            entity.setPassword(encoder.encode(PRIMARY_PASSWORD));
        } else {
            entity.setPassword(encoder.encode(entity.getPassword()));
        }
        return super.save(entity);
    }

    @Override
    public boolean saveBatch(Collection<User> modelList, int batchSize) {
        modelList.forEach(model -> {
            if (model.getPassword() == null) {
                model.setPassword(encoder.encode(PRIMARY_PASSWORD));
            } else {
                model.setPassword(encoder.encode(model.getPassword()));
            }
        });
        return super.saveBatch(modelList, batchSize);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean removeById(Long id) {
        User user = mapper.selectById(id);
        LambdaUpdateWrapper<User> wrapper = Wrappers.<User>lambdaUpdate()
                .eq(User::getId, user.getId())
                .set(User::getUsername, user.getId())
                .set(User::getStatus, 2)
                .set(User::getUpdateUser, currentUserHandler.getUsername())
                .set(User::getUpdateTime, LocalDateTime.now())
                .set(User::getDeleted, true);
        boolean ans = super.update(wrapper);
        if (ans && getModelCache() != null) {
            getModelCache().deleteCache(id);
        }
        return ans;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean removeByIds(Collection<? extends Long> idList) {
        List<User> users = mapper.selectBatchIds(idList);
        users.forEach(user -> {
            user.setUsername(String.valueOf(user.getId()));
            user.setStatus(2);
        });
        boolean ans = super.updateBatchById(users);
        if (ans) {
            LambdaUpdateWrapper<User> wrapper = Wrappers.<User>lambdaUpdate()
                    .in(User::getId, users.stream().map(User::getId).collect(Collectors.toSet()))
                    .set(User::getUpdateUser, currentUserHandler.getUsername())
                    .set(User::getUpdateTime, LocalDateTime.now())
                    .set(User::getDeleted, true);
            ans = super.update(wrapper);
            if (ans && getModelCache() != null) {
                getModelCache().deleteCaches(idList);
            }
            return ans;
        } else {
            throw new GlobalException(Status.UPDATE_FAILURE, HttpStatus.BAD_REQUEST);
        }
    }

    @Override
    public boolean resetPassword(Long id) {
        return super.updateById(User.builder()
                .id(id)
                .password(encoder.encode(PRIMARY_PASSWORD))
                .build());
    }

    @Override
    public User createModel() {
        return new User();
    }
}
